﻿/*
--------------------------------------
File: BlobUtils.cs
Project: Themaopdracht 7
Author: Maarten Kroon

Description:
Static blob functions, that were before in blobDetection.cs
--------------------------------------
*/

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace THO7_Team8
{
    internal static class BlobUtils
    {
        //List to store the blobs
        static List<Blob> blobList;

        //Two lists used for the blobdetection
        static List<int> checkList1;
        static List<int> checkList2;

        //List for the filtered blobs
        static List<int> goodBlobList;

        static int blobSize;

        //A struct used for blobs, has size and label attributes
        public struct Blob
        {
            public int size, label,

            //Blobs dimensions
            firstXx, firstXy,
            lastXx, lastXy,
            firstYx, firstYy,
            lastYx, lastYy;

            public Blob(int nwSize, int nwLabel)
            {
                size = nwSize;
                label = nwLabel;

                firstXx = 0; firstXy = 0;
                lastXx = 0; lastXy = 0;
                firstYx = 0; firstYy = 0;
                lastYx = 0; lastYy = 0;
            }
        }

        /// <summary>
        /// Resets all the static class variables
        /// </summary>
        public static void initialize()
        {
            blobList = new List<Blob>();
            checkList1 = new List<int>();
            checkList2 = new List<int>();
            goodBlobList = new List<int>();
            blobSize = 0;
        }

        /// <summary>
        /// Loops through the image and labels blobs
        /// </summary>
        /// <param name="array">int array of the image</param>
        /// <param name="blobColor">color to look for, 32bits rgb int</param>
        public static void findBlobs(ref int[] array, int blobColor, int width)
        {
            int label = 0;

            for (int i = 0; i < array.Length; i++)
            {
                if (array[i] == blobColor)
                {
                    array[i] = label;
                    blobSize++;

                    findNeighbours(i, label, 1, blobColor, ref array, width);
                    processList(label, blobColor, ref array, width);

                    Blob b = new Blob(blobSize, label);
                    blobList.Add(b);
                    blobSize = 0;

                    label++;
                }
            }
        }

        public static void colorTargetBlob(ref int[] array, int blobColor, int nwColor, int width, int position)
        {
            array[position] = nwColor;
            blobSize++;
            findNeighbours(position, nwColor, 1, blobColor, ref array, width);
            processList(nwColor, blobColor, ref array, width);
        }

        //Find the neighbour pixels of position and adds them to the blob        
        public static void findNeighbours(int position, int label, int activeList, int blobColor, ref int[] array, int width)
        {
            //Pixel to the left of position
            if (position >= 0 + 1)
            {
                if (array[position - 1] == blobColor)
                {
                    array[position - 1] = label;
                    if (activeList == 1)
                    {
                        checkList1.Add(position - 1);
                    }
                    else
                    {
                        checkList2.Add(position - 1);
                    }
                    blobSize++;
                }
            }
            //Pixel to the right of position
            if (position < array.Length - 1)
            {
                if (array[position + 1] == blobColor)
                {
                    array[position + 1] = label;
                    if (activeList == 1)
                    {
                        checkList1.Add(position + 1);
                    }
                    else
                    {
                        checkList2.Add(position + 1);
                    }
                    blobSize++;
                }
            }
            if (position >= 0 + width)
            {
                //Pixel above the position
                if (array[position - width] == blobColor)
                {
                    array[position - width] = label;
                    if (activeList == 1)
                    {
                        checkList1.Add(position - width);
                    }
                    else
                    {
                        checkList2.Add(position - width);
                    }
                    blobSize++;
                }
            }
            if (position < array.Length - width)
            {
                //Pixel below the position
                if (array[position + width] == blobColor)
                {
                    array[position + width] = label;
                    if (activeList == 1)
                    {
                        checkList1.Add(position + width);
                    }
                    else
                    {
                        checkList2.Add(position + width);
                    }
                    blobSize++;
                }
            }
        }

        //When a blobColored pixel is found, loops through the lists until all pixels are labeled
        public static void processList(int label, int blobColor, ref int[] array, int width)
        {
            Boolean hasElements = true;
            while (hasElements == true)
            {
                hasElements = false;
                foreach (int i in checkList1)
                {
                    findNeighbours(i, label, 2, blobColor, ref array, width);
                    hasElements = true;
                }
                checkList1.Clear();
                foreach (int i in checkList2)
                {
                    findNeighbours(i, label, 1, blobColor, ref array, width);
                    hasElements = true;
                }
                checkList2.Clear();
            }
        }

        //Finds the blobs edges
        public static Blob findBlobDimensions(Blob b, int width, ref int[] array)
        {
            int firstYy = 0, firstYx = 0;
            int firstXy = width, firstXx = width;
            int lastYy = 0, lastYx = 0;
            int lastXy = 0, lastXx = 0;

            for (int i = 0; i < array.Length; i++)
            {
                if (array[i] == b.label)
                {
                    //The first Y is the first blob pixel
                    if (firstYy == 0)
                    {
                        firstYy = i / width;
                        firstYx = i % width;
                    }
                    //The first X is the most left blob pixel
                    if (firstXx > (i % width))
                    {
                        firstXy = i / width;
                        firstXx = i % width;
                    }
                    //The last X pixel is the most right blob pixel
                    if ((i % width) > (lastXx % width))
                    {
                        lastXx = i % width;
                        lastXy = i / width;
                    }
                    //The last Y pixel is the last blob pixel
                    lastYy = i / width;
                    lastYx = i % width;
                }
            }
            b.firstXx = firstXx; b.firstXy = firstXy;
            b.lastXx = lastXx; b.lastXy = lastXy;
            b.firstYx = firstXy; b.firstYy = firstYy;
            b.lastYx = lastYx; b.lastYy = lastYy;

            return b;
        }

        //Filters blobs that are higher than wide
        public static void filterDimensions(int width, ref int[] array)
        {
            //Creates a tempList for when the code below filters all the blobs
            List<int> tempBlobList = new List<int>(goodBlobList);

            for (int i = 0; i < blobList.Count; i++)
            //foreach (Blob b in blobList)
            {
                Blob b = blobList[i];
                if (goodBlobList.Contains(b.label))
                {
                    b = findBlobDimensions(b, width, ref array);

                    int A, B, shortSide, longSide;
                    double C;

                    //License plate is rotated to the left
                    if (b.firstYx > b.lastYx)
                    {
                        //Distance between firstX and lastY (short side)
                        A = b.lastYx - b.firstXx;
                        B = b.lastYy - b.firstXy;
                        C = Math.Sqrt(Math.Pow(A, 2) + Math.Pow(B, 2));
                        shortSide = (int)C;

                        //Distance between firstX and firstY (long side)
                        A = b.firstYx - b.firstXx;
                        B = b.firstYy - b.firstXy;
                        C = Math.Sqrt(Math.Pow(A, 2) + Math.Pow(B, 2));
                        longSide = (int)C;
                    }
                    //License plate is rotated to the right
                    else
                    {
                        //Distance between firstY and firstX (short side)
                        A = b.firstYy - b.firstXy;
                        B = b.firstYx - b.firstXx;
                        C = Math.Sqrt(Math.Pow(A, 2) + Math.Pow(B, 2));
                        shortSide = (int)C;

                        //Distance between firstY and firstX (long side)
                        A = b.lastYx - b.firstXx;
                        B = b.lastYy - b.firstXy;
                        C = Math.Sqrt(Math.Pow(A, 2) + Math.Pow(B, 2));
                        longSide = (int)C;
                    }

                    //If the blob dimmensions are wrong, remove it from the goodBlobList
                    if (longSide / shortSide < 3 || longSide / shortSide > 5)
                    {
                        goodBlobList.Remove(b.label);
                    }
                }
            }

            //If all blobs are filtered because of their dimensions there might be a distortion
            //Restore the goodBlobList with the previously made backup
            if (goodBlobList.Count < 1)
            {
                goodBlobList = tempBlobList;
            }

            //Filters the blob dimensions, but in a much more simple way than above
            foreach (Blob b in blobList)
            {
                if (goodBlobList.Contains(b.label))
                {
                    if (((b.firstXx - b.lastXx) * 2) < (b.firstYy - b.lastYy))
                    {
                        goodBlobList.Remove(b.label);
                    }
                }
            }
        }

        public static int getGoodBlobListSize()
        {
            return goodBlobList.Count();
        }

        //Filters blobs which are to small to be a license plate
        public static void filterSmallBlobs(double minSize)
        {
            //Creates a list which blobs that are large enough

            foreach (Blob b in blobList)
            {
                if (b.size > minSize)
                {
                    goodBlobList.Add(b.label);
                }
            }
        }

        //Filters all but the biggest blob if there are multiple blobs in the goodBlobList
        public static void multipleBlobFilter()
        {
            int biggestBlob = 0;
            int biggestBlobLabel = 0;

            if (goodBlobList.Count > 1)
            {
                foreach (Blob b in blobList)
                {
                    if (goodBlobList.Contains(b.label))
                    {
                        if (b.size > biggestBlob)
                        {
                            biggestBlob = b.size;
                            biggestBlobLabel = b.label;
                        }
                    }
                }
                for (int i = 0; i < goodBlobList.Count; i++)
                {
                    if (goodBlobList[i] != biggestBlobLabel)
                    {
                        goodBlobList.Remove(goodBlobList[i]);
                    }
                }
            }
        }

        //Filters all the blobs that are not in the goodBlobList
        public static void filterBlobs(ref int[] array)
        {
            //Converts the list to an array, because this is much faster
            int[] goodBlobArray = new int[goodBlobList.Count];
            for (int i = 0; i < goodBlobArray.Length; i++)
            {
                goodBlobArray[i] = goodBlobList[i];
            }

            //Checks for each pixel if the blob is in the goodBlobArray
            for (int i = 0; i < array.Length; i++)
            {
                bool goodBlob = false;
                for (int j = 0; j < goodBlobArray.Length; j++)
                {
                    {
                        if (array[i] == goodBlobList[j])
                        {
                            goodBlob = true;
                        }
                    }
                }
                //If the blob is not in the goodBlobArray, makes it black
                if (goodBlob == false)
                {
                    array[i] = -16777216;
                }
                else
                {
                    array[i] = -1;
                }
            }
        }

        public static List<Bitmap> getCharacters(ref int[] array, int width)
        {
            List<Bitmap> characters = new List<Bitmap>();

            //foreach (Blob b in blobList)
            for (int i = 0; i < blobList.Count; i++)
            {
                Blob b = blobList[i];
            
                if (goodBlobList.Contains(b.label))
                {
                    b = findBlobDimensions(b, width, ref array);

                    Bitmap character = new Bitmap(b.lastXx - b.firstXx, b.lastYy - b.firstYy);
                    int[] characterArray = MyImage.BmpToArray(character);

                    //Crops the character
                    int counter = 0;
                    for (int y = b.firstYy; y < b.lastYy; y++)
                    {
                        for (int x = b.firstXx; x < b.lastXx; x++)
                        {
                            characterArray[counter] = array[y * width + x];
                            counter++;
                        }
                    }
                    character = MyImage.ArrayToBmp(characterArray, character);
                    characters.Add(character);
                }
            }
            return characters;
        }

        //Recursive function to create blobs, may create stack overflows on big images
        public static void createBlob(int position, int blobColor, int label, ref int[] array, int width)
        {
            //Checks if the position is in the array
            if (position < array.Length && position >= 0)
            {
                //If the current pixel in the array is white
                if (array[position] == blobColor)
                {
                    blobSize++;
                    //Labels the blob by changing its color
                    array[position] = label;

                    //Checks the neighbour pixels of the current pixel
                    createBlob(position - 1, blobColor, label, ref array, width);
                    createBlob(position + 1, blobColor, label, ref array, width);
                    createBlob(position - width, blobColor, label, ref array, width);
                    createBlob(position + width, blobColor, label, ref array, width);
                }
            }
        }
    }
}